﻿using System;
using System.Threading;

namespace LogNaga.Logs
{
	/// <summary>
	/// 提供线程安全的机制来执行某种<see cref="Log"/>
	/// </summary>
	public class ThreadSafeLogDecorator : Log, IDisposable
	{
		private readonly TimeSpan waitTime;	//每次对锁的等待时间
		private readonly object SyncRoot;
		private volatile bool disposed;
		private Log toLockingLog;	//要locking的Log 

		#region .ctor

		/// <summary>
		/// 初始化一个<see cref="Log"/>实例
		/// </summary>
		/// <param name="toLockingLog">被加锁的Log实例</param>
		public ThreadSafeLogDecorator(Log toLockingLog)
			: this(toLockingLog, TimeSpan.FromSeconds(1))
		{
		}

		/// <summary>
		/// 初始化一个<see cref="Log"/>实例
		/// </summary>
		/// <param name="toLockingLog">被加锁的Log实例</param>
		/// <param name="waitTime">锁的时候的最长等待时间</param>
		public ThreadSafeLogDecorator(Log toLockingLog, TimeSpan waitTime)
		{
			if (toLockingLog == null)
				throw new ArgumentNullException("toLockingLog");

			this.waitTime = waitTime;
			this.toLockingLog = toLockingLog;
			this.disposed = false;
			this.SyncRoot = new object();
		}

		#endregion

		/// <summary>
		/// 写入信息
		/// </summary>
		/// <param name="errorEntry"><see cref="Error"/>实例</param>
		public override void Write(Error errorEntry)
		{
			if (this.disposed)
			{
				throw new ObjectDisposedException(ToString());
			}

			this.LockAndExecute(() => this.toLockingLog.Write(errorEntry));
		}

		/// <summary>
		/// 锁住syncRoot然后执行操作
		/// </summary>
		/// <param name="action"></param>
		private void LockAndExecute(Action action)
		{
			if (Monitor.TryEnter(this.SyncRoot, this.waitTime))
			{
				try
				{
					action();
				}
				finally
				{
					Monitor.Exit(this.SyncRoot);
				}
			}
		}

		#region IDisposable Members

		/// <summary>
		/// Dispose
		/// </summary>
		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Dispose实现
		/// </summary>
		/// <param name="disposing"></param>
		private void Dispose(bool disposing)
		{
			if (this.disposed)
			{
				return;
			}

			if (disposing)
			{
				Action cleanup = () =>
				{
					try
					{
						IDisposable disposable = this.toLockingLog as IDisposable;
						if (disposable != null)
						{
							disposable.Dispose();
						}

						this.toLockingLog = null;
					}
					finally
					{
						this.disposed = true;
					}
				};

				this.LockAndExecute(cleanup);
			}
		}

		#endregion

		/// <summary>
		/// 为调试方便override此ToString方法
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return this.GetType().FullName + "(" + this.toLockingLog.GetType().FullName + ")";
		}
	}
}
